#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <sys/syscall.h>   /* For SYS_write etc */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const long long_size = sizeof(long);

/* 翻转字符串 */
void reverse(char *str, int len)
{   
    int i, j;
    char temp;
    for(i = 0, j = len - 1; i < j; ++i, --j) 
	{
        temp = str[i];
        str[i] = str[j];
        str[j] = temp;
    }
}
/* 从子进程的addr取出len字节的输入存放到str */
void getdata(pid_t child, long addr, char *str, int len)
{   
    int cp_size;
    union u {
        long val;
        char chars[long_size];
    }data;

	while(len > 0) 
	{
        data.val = ptrace(PTRACE_PEEKDATA, child, addr, NULL);
        if (len < long_size) cp_size = len;
			else cp_size = long_size;
		memcpy(str, data.chars, cp_size);
        str += cp_size;
		addr += long_size;
		len -= long_size;
    }
}

/* 向子进程的addr写入len个字节数据，数据源为str */
void putdata(pid_t child, long addr, char *str, int len)
{   
    int cp_size;
    union u {
        long val;
        char chars[long_size];
    }data;

    while(len > 0) 
	{
		if (len < long_size) cp_size = len;
			else cp_size = long_size;
        memcpy(data.chars, str, cp_size);
        ptrace(PTRACE_POKEDATA, child, addr, data.val);
        str += cp_size;
		addr += long_size;
		len -= long_size;
    }
}

int main()
{   
    pid_t child;
    long orig_eax, eax;
    //long params[3];
    int status;
	/* 记录目前是准备进入系统调用，还是准备从系统调用返回 */
    int insyscall = 0;
	struct user_regs_struct regs;
    
	child = fork();
    if(child == 0) 
	{
		/* child */
		/* 告诉system:本进程被其父进程所跟踪。其父进程应该希望跟踪子进程 */
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/bin/ls", "ls", NULL);
    }
    else 
	{
		/* father */
		while(1) 
		{
			wait(&status);
			if ( WIFEXITED(status) ) break;
			/* ORIG_EAX define in sys/reg.h */
			/* ptrace(PTRACE_PEEKUSR, pid, addr, data)
			 * 从USER 区域中读取数据,pid 表示被跟踪的子进程,USER 区域地址由 addr
			 * 给出,data 为用户变量地址用于返回读到的数据。
			 * */
			orig_eax = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);
			/* write syscall */
			if(orig_eax == SYS_write) 
			{
				if(insyscall == 0) 
				{    
					/* Syscall entry */
					insyscall = 1;
					/*
					 * 使用PTRACE_GETREGS更方便
					params[0] = ptrace(PTRACE_PEEKUSER, child, 4 * EBX, NULL);
					params[1] = ptrace(PTRACE_PEEKUSER, child, 4 * ECX, NULL);
					params[2] = ptrace(PTRACE_PEEKUSER, child, 4 * EDX, NULL);
					printf("Write called with %ld, %lu, %ld \n", params[0], params[1], params[2]);
					*/
					/* PTRACE_GETREGS 为获取寄存器的值 */
					ptrace(PTRACE_GETREGS, child, NULL, &regs);
					printf("Write called with %ld, %lu, %ld \n",regs.ebx, regs.ecx, regs.edx);
				
					/* 翻转输出内容 */	
					int len = regs.edx; /* SYS_write时 edx是长度 ecx是地址 */
					char * str = (char *) malloc(len);
					getdata(child, regs.ecx, str, len);
					reverse(str, len);
					putdata(child, regs.ecx, str, len);
				}
				else 
				{ /* Syscall exit */
					eax = ptrace(PTRACE_PEEKUSER, child, 4 * EAX, NULL);
					printf("Write returned with %ld\n", eax);
					insyscall = 0;
				}
			}
			ptrace(PTRACE_SYSCALL, child, NULL, NULL);
		}
    }
    return 0;
}
